#include <stdlib.h>
#include <stdio.h>

#define HEAP_START 0x400000
#define HEAP_END   0x900000
#define HEAP_SIZE  0x100000
#define AVIABLE_VARIABLES 1000
#define TASK_SPACE 0x200000


#define MEMORY_PRESENT      0x1
#define PAGE_WRITEABLE      0x2
#define EVERYONE_ACCESSABLE 0x4
#define WRITE_THROUGH_ON 	 0x8
#define CACHING_OFF			 0x10

unsigned int ende = HEAP_START;
unsigned int taskCount = 1;

struct kernelPagingStructure kernelPageMap;

struct kernelPagingStructure {
    unsigned int *pageDirectory;
    unsigned int *pageTable[1024];
    unsigned short aktPageDirectory;
    unsigned short aktPageTable;
    unsigned int realAddr;
};



void *getFreeMemory() {
    return malloc(0x1000);
}

void analyse(struct kernelPagingStructure str, char sign) {
    int i, j;
    for (i = 0; i < 1024; i++) {
        if (str.pageTable[i] != ((void *)0)) {
            for (j = 0; j < 1024; j++)
                printf("%c PageTable [%d][%d] = %x\n",sign, i,j, str.pageTable[i][j]);
        }
    }
}

void mapPage(unsigned int physMemory, struct kernelPagingStructure *str) {
    int pageFrames = physMemory / 0x1000;

    int pageFrameIndex = pageFrames % 1024;
    pageFrames /= 1024;
    int pageTableIndex = pageFrames % 1024;

    if (str->pageTable[pageTableIndex] == ((void *)0)) {
        str->pageTable[pageTableIndex] = getFreeMemory();
        printf("PageTable Adress = %x\n", (unsigned int)kernelPageMap.pageTable[pageTableIndex]);
    }

    str->pageTable[pageTableIndex][pageFrameIndex] = physMemory;
    str->pageDirectory[pageTableIndex] = (unsigned int)kernelPageMap.pageTable[pageTableIndex];
}

void initPaging() {
    unsigned int count;
    kernelPageMap.pageDirectory = getFreeMemory();
    printf("PageDirectory Adress = %x\n", (unsigned int)kernelPageMap.pageDirectory);



    for (count = 0; count < 1024; count++) {
        kernelPageMap.pageTable[count] = ((void *)0);
    }

    for (count = 0; count < ende;) {
        mapPage(count, &kernelPageMap);
        count += 0x1000;
    }

}

void initHeap() {
    /*map the space from 0x400000 to 0x500000*/
    int count = HEAP_START;
    while (count < HEAP_END) {
        mapPage(count, &kernelPageMap);
        count += 0x1000;
    }

}

void mapPageE(unsigned int virtMemory, unsigned int physMemory, struct kernelPagingStructure *str) {
    int pageFrames = virtMemory / 0x1000;

    int pageFrameIndex = pageFrames % 1024;
    pageFrames /= 1024;
    int pageTableIndex = pageFrames % 1024;

    if (str->pageTable[pageTableIndex] == ((void *)0)) {
        str->pageTable[pageTableIndex] = getFreeMemory();
        printf("PageTable Adress = %x\n", (unsigned int)str->pageTable[pageTableIndex]);
    }

    str->pageTable[pageTableIndex][pageFrameIndex] = physMemory;
    str->pageDirectory[pageTableIndex] = (unsigned int)str->pageTable[pageTableIndex];
}

struct kernelPagingStructure makePageDirForTask() {
    struct kernelPagingStructure vAddrRoom;

    vAddrRoom.pageDirectory = getFreeMemory();
    printf("PageDirectory Adress = %x\n", (unsigned int)vAddrRoom.pageDirectory);

    int count = HEAP_START;

    for (count = 0; count < 1024; count++) {
        vAddrRoom.pageTable[count] = ((void *)0);
    }

    for (count = 0; count < ende;) {
        mapPage(count, &vAddrRoom);
        count += 0x1000;
    }

    count = HEAP_START;

    while (count < HEAP_END) {
        mapPage(count, &vAddrRoom);
        count += 0x1000;
    }

    /*
    count = HEAP_END + (taskCount * TASK_SPACE);
    int c2 = HEAP_END;
    while(count <= ((HEAP_END + (taskCount * TASK_SPACE)) + TASK_SPACE)){
       mapPage(c2, &vAddrRoom);
     	mapPage(c2, &kernelPageMap);
       c2 += 0x1000;
       count += 0x1000;
    }
    */
    count = HEAP_END + (taskCount * TASK_SPACE);
    while (count < ((HEAP_END + (taskCount * TASK_SPACE)) + TASK_SPACE)) {
        mapPage(count, &kernelPageMap);
        count += 0x1000;
    }

    count = HEAP_END;
    while (count < (HEAP_END + TASK_SPACE - (TASK_SPACE / 2))) {
        mapPageE(count, count + (taskCount * TASK_SPACE), &vAddrRoom);
        count += 0x1000;
    }

    while (count < HEAP_END + (taskCount * TASK_SPACE) + TASK_SPACE) {
        mapPage(count, &vAddrRoom);
        count += 1000;
    }

    return vAddrRoom;
}

int main(void) {
    initPaging();
    initHeap();

    struct kernelPagingStructure buf = makePageDirForTask();
    printf("-------------------------------\n");
    printf("Task\n");
    analyse(buf, 'T');
    printf("-------------------------------\n");
    analyse(kernelPageMap, 'K');
}
